package junit.source.pattern.listener;

import org.junit.runner.Description;
import org.junit.runner.notification.Failure;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static java.util.Arrays.asList;

public class RunNotifier {

    List<RunListener> listeners = new CopyOnWriteArrayList<>();

    public void addListener(RunListener listener) {
        if (listener == null) {
            throw new NullPointerException("Cannot add a null listener");
        }

        listeners.add(listener);
    }

    /**
     * run方法执行的目的：是为了遍历listeners （List<RunListener>），执行对应的方法，暴露notifyListener方法，通过匿名子类来实现不同的周期方法。
     * safe 的意义在于，list是 CopyOnWriteArrayList
     * 很优雅的实现不同方法的调用抽象
     */
    private abstract class SafeNotifier {
        List<RunListener> currentListeners;

        SafeNotifier() {
            currentListeners = listeners;
        }

        SafeNotifier(List<RunListener> listeners) {
            currentListeners = listeners;
        }

        void run() {
            for (RunListener runListener : currentListeners) {
                try {
                    notifyListener(runListener);
                } catch (Exception e) {
                    // do business sth
                    e.printStackTrace();
                }
            }
        }

        protected abstract void notifyListener(RunListener runListener) throws Exception;
    }

    public void fireTestRunStarted(List<RunListener> listeners, List<Description> descriptions) {
        new SafeNotifier(listeners) {

            @Override
            protected void notifyListener(RunListener runListener) throws Exception {
                for (Description description : descriptions) {
                    runListener.testRunStarted(description);
                }
            }
        }.run();
    }

    public void fireTestSuiteStarted(final Description description) {
        new SafeNotifier() {

            @Override
            protected void notifyListener(RunListener runListener) throws Exception {
                runListener.testSuiteStarted(description);
            }
        }.run();
    }

    public void fireTestFailure(Failure failure) {
        fireTestFailures(listeners, asList(failure));
    }

    public void fireTestFailures(List<RunListener> listeners, final List<Failure> failures) {
        new SafeNotifier(listeners) {
            protected void notifyListener(RunListener listener) throws Exception {
                for (Failure each : failures) {
                    listener.testFailure(each);
                }
            }
        }.run();
    }
}
